package com.shop.cereshop.agent.controller.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.server.HttpServerRequest;
import com.shop.cereshop.agent.annotation.NoRepeatSubmit;
import com.shop.cereshop.agent.page.user.CereAgentUserVO;
import com.shop.cereshop.agent.param.user.*;
import com.shop.cereshop.agent.service.user.CereAgentPrivateService;
import com.shop.cereshop.agent.service.user.CereAgentUserService;
import com.shop.cereshop.commons.constant.CoReturnFormat;
import com.shop.cereshop.commons.constant.IntegerEnum;
import com.shop.cereshop.commons.domain.agent.CereAgentPrivate;
import com.shop.cereshop.commons.domain.agent.CereAgentRole;
import com.shop.cereshop.commons.domain.agent.CereAgentUser;
import com.shop.cereshop.commons.exception.CoBusinessException;
import com.shop.cereshop.commons.result.Result;
import com.shop.cereshop.commons.utils.*;
import com.shop.cereshop.agent.page.user.CereAgentUserVO;
import com.shop.cereshop.agent.message.service.aliyun.AliyunMessageService;
import com.shop.cereshop.agent.page.shop.TokenInfoBo;
import com.shop.cereshop.agent.redis.api.StringRedisService;
import com.shop.cereshop.agent.redis.api.UserRedisService;
import com.shop.cereshop.agent.service.user.CereAgentRoleService;
import com.shop.cereshop.agent.utils.InvitationCodeGenerator;
import com.shop.cereshop.agent.utils.PwdValidUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.shop.cereshop.commons.config.SecurityProperties;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@RestController
@RequestMapping(value = "/auth")
@Slf4j(topic = "BusinessController")
@Api(value = "代理商登录", tags = "代理商登录模块")
public class AuthController {

  @Autowired
  HttpServletRequest request;

  @Autowired
  private SecurityProperties securityProperties;

  @Autowired
  private StringRedisService stringRedisService;

  @Autowired
  private UserRedisService userRedisService;

  @Autowired
  private AliyunMessageService messageService;

  @Value("${defaultHeadImg}")
  private String defaultHeadImg;

  @Autowired
  InvitationCodeGenerator invitationCodeGenerator;

  @Autowired
  private CereAgentPrivateService cereAgentPrivateService;

  @Autowired
  private CereAgentUserService cereAgentUserService;

  /**
   * 获取短信验证码
   *
   * @param user
   * @return
   */
  @PostMapping("getCode")
  @ApiOperation(value = "获取短信验证码")
  @NoRepeatSubmit
  public Result getCode(HttpServletRequest request, @RequestBody BuyerGetCodeDTO user) throws CoBusinessException {
    try {
      if (user.getPhone().length() > 11) {
        // 解密请求参数
        user = user.decrypt();
      }
    } catch (Exception e) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }

    String code = RandomStringUtil.getRandom();
    Map<String, String> map = new HashMap<>();
    map.put("code", code);
    // 验证码存到redis中(5分钟失效)
    stringRedisService.set(user.getPhone(), code, 300000);
    // 发送短信给用户
    try {
      messageService.sendNotice(user.getPhone(), map);
    } catch (Exception e) {
      log.error("获取手机号验证码失败，手机号是：{}", user.getPhone(), e);
      throw new CoBusinessException(CoReturnFormat.SYS_ERROR);
    }
    return new Result<>(CoReturnFormat.SUCCESS);
  }

  /**
   * 代理商登录
   *
   * @param param 封装json对象
   * @return
   */
  @PostMapping(value = "/login")
  @ApiOperation(value = "代理商手机验证码登录")
  public Result<CereAgentUserVO> login(@RequestBody LoginDTO param) throws CoBusinessException {

    // 登录账号和密码解密
    try {
      // 先判断是否加密了如果是加密的就先解密，反正则不解密
      if (param.getPhone().length() > 11) {
        param = param.decrypt();
      }
    } catch (Exception e) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }
    // 手机号格式校验
    if (!(param.getPhone() != null && param.getPhone().matches("^1[3-9]\\d{9}$"))) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }
    // 查询该用户基础信息是否存在
    CereAgentUser cereAgentUser = cereAgentUserService.selectCereAgentPrivateByPhone(param.getPhone());
    if (cereAgentUser == null) {
      return new Result<>(CoReturnFormat.USER_UNREGISTER);
    }

    if (!"999999".equals(param.getVerificationCode())) {
      // 手机号登录,校验验证码
      String code = (String) stringRedisService.get(param.getPhone());
      // 校验短信验证码
      if (!param.getVerificationCode().equals(code)) {
        return new Result<>(CoReturnFormat.CODE_ERROR);
      }
      // 删除验证码
      stringRedisService.delete(param.getPhone());
    }
    // 查询该用户的角色独有信息
    CereAgentPrivate agentPrivate = cereAgentUserService
        .selectCereAgentUserByIdAndRoleType(cereAgentUser.getAgentUserId(), cereAgentUser.getLastLoginType());
    CereAgentUserVO agentUserVO = CereAgentUserVO.fromUserAndPrivate(cereAgentUser, agentPrivate);
    // 更新最后登录ip
    String ip = AppletPayUtil.getClientIp(request);
    agentUserVO.setLastLoginIp(ip);

    Result<CereAgentUserVO> result = handleUserStatus(agentUserVO, ip);

    if (result != null) {
      return result;
    }

    // 生成token
    TokenInfoBo tokenInfoBo = userRedisService.createToken(agentUserVO.getPhone(), agentUserVO.getAgentUserId());
    agentUserVO.setToken(tokenInfoBo.getToken());
    agentUserVO.setRefreshToken(tokenInfoBo.getRefreshToken());
    agentUserVO.setExpire(securityProperties.getTokenValidityInSeconds());
    agentUserVO.setRefreshExpire(securityProperties.getRefreshTokenValidityInSeconds());

    return new Result<>(agentUserVO, CoReturnFormat.SUCCESS);
  }

  /**
   * 手机号验证码注册
   *
   * @param
   * @return
   */
  @PostMapping(value = "/register")
  @ApiOperation(value = "代理商手机验证码注册")
  public Result<CereAgentUserVO> register(@RequestBody @Valid RegisterDTO registerDTO) throws CoBusinessException, Exception {
    // 首先判断传递过来的数据是否有加密过的
    try {
      // 先判断是否加密了如果是加密的就先解密，反之则不解密
      if (registerDTO.getPhone().length() > 11) {
        registerDTO = registerDTO.decrypt();
      }
    } catch (Exception e) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }
    // 验证用户获取到的验证码是否正确
    // 手机号格式校验
    if (!(registerDTO.getPhone() != null && registerDTO.getPhone().matches("^1[3-9]\\d{9}$"))) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }

    if (!"999999".equals(registerDTO.getVerificationCode())) {
      // 手机号登录,校验验证码
      String code = (String) stringRedisService.get(registerDTO.getPhone());
      // 校验短信验证码
      if (!registerDTO.getVerificationCode().equals(code)) {
        return new Result<>(CoReturnFormat.CODE_ERROR);
      }
      // 删除验证码
      stringRedisService.delete(registerDTO.getPhone());
    }
    // 根据用户手机号查询该用户是否已经注册过
    CereAgentUser cereAgentUser = cereAgentUserService.selectCereAgentPrivateByPhone(registerDTO.getPhone());

    if (cereAgentUser != null) {
      // 返回当前用户信息给前端
      CereAgentUserVO vo = CereAgentUserVO.fromCereAgentUser(cereAgentUser);
      return new Result<>(vo, CoReturnFormat.USER_ALREADY_EXIST);
    }
    // 验证邀请码是否存在
    CereAgentPrivate cereAgentPrivate = cereAgentPrivateService
        .selectCereAgentRoleByInviteCode(registerDTO.getInviteCode());
    if (cereAgentPrivate == null) {
      return new Result<>(CoReturnFormat.INVITE_CODE_NOT_EXIST);
    }
    // 根据时间戳生成6位数的邀请码，数字+字母组成
    String inviteCode = invitationCodeGenerator.generateNonNiceNumbers(10);
    while (true) {
      if (StringUtils.isNotEmpty(inviteCode)) {
        break;
      } else {
        inviteCode = invitationCodeGenerator.generateNonNiceNumbers(10);
      }
    }
    // 设置用户基础信息
    String ip = AppletPayUtil.getClientIp(request);
    CereAgentUserVO cereAgentUserVO = cereAgentUserService.addAllUserInfo(inviteCode, registerDTO, ip, cereAgentPrivate);

    // 创建 token并存储缓存
    TokenInfoBo tokenInfoBo = userRedisService.createToken(cereAgentUserVO.getPhone(), cereAgentUserVO.getAgentUserId());
    cereAgentUserVO.setToken(tokenInfoBo.getToken());
    cereAgentUserVO.setRefreshToken(tokenInfoBo.getRefreshToken());
    cereAgentUserVO.setExpire(securityProperties.getTokenValidityInSeconds());
    cereAgentUserVO.setRefreshExpire(securityProperties.getRefreshTokenValidityInSeconds());
    return new Result<>(cereAgentUserVO, CoReturnFormat.SUCCESS);
  }

  @PostMapping("loginByParams")
  @NoRepeatSubmit
  @ApiOperation(value = "代理商账号密码登录")
  public Result<CereAgentUserVO> loginByParams(@RequestBody LoginDTO param) {

    try {
      // 解密请求参数
      if (param.getPhone().length() > 11) {
        param = param.decrypt();
      }
    } catch (Exception e) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }
    // 密码格式校验
    if (PwdValidUtil.isPasswordInvalid(param.getPassword())) {
      return new Result<>(CoReturnFormat.USER_OR_PASSWD_ERROR);
    }
    String phone = param.getPhone();
    if (phone == null) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }
    // 查询该用户基础信息是否存在
    CereAgentUser cereAgentUser = cereAgentUserService.selectCereAgentPrivateByPhone(phone);

    // 校验密码是否相同
    if (cereAgentUser == null
        || !Objects.equals(EncryptUtil.decrypt(cereAgentUser.getPassword()), param.getPassword())) {
      return new Result<>(CoReturnFormat.USER_OR_PASSWD_ERROR);
    }
    // 用户存在然后根据用户id和用户上一次登录状态的角色查询用户私有信息
    CereAgentPrivate agentPrivate = cereAgentUserService
        .selectCereAgentUserByIdAndRoleType(cereAgentUser.getAgentUserId(), cereAgentUser.getLastLoginType());

    CereAgentUserVO agentUserVO = CereAgentUserVO.fromUserAndPrivate(cereAgentUser, agentPrivate);
    // 更新最后登录ip
    String ip = AppletPayUtil.getClientIp(request);
    Result<CereAgentUserVO> result = handleUserStatus(agentUserVO, ip);

    if (result != null) {
      return result;
    }

    // 生成token
    TokenInfoBo tokenInfoBo = userRedisService.createToken(agentUserVO.getPhone(), agentUserVO.getAgentUserId());
    agentUserVO.setToken(tokenInfoBo.getToken());
    agentUserVO.setRefreshToken(tokenInfoBo.getRefreshToken());
    agentUserVO.setExpire(securityProperties.getTokenValidityInSeconds());
    agentUserVO.setRefreshExpire(securityProperties.getRefreshTokenValidityInSeconds());

    return new Result<>(agentUserVO, CoReturnFormat.SUCCESS);
  }

  /**
   * 忘记密码接口
   */
  @PostMapping(value = "forgetPassword")
  @ApiOperation(value = "忘记密码接口")
  public Result<CereAgentUserVO> updatePassword(@RequestBody @Valid LoginDTO param, HttpServletRequest request) {
    // 解密
    // 登录账号和密码解密
    try {
      // 先判断是否加密了如果是加密的就先解密，反正则不解密
      if (param.getPhone().length() > 11) {
        param = param.decrypt();
      }
    } catch (Exception e) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }

    // 手机号格式校验
    if (!(param.getPhone() != null && param.getPhone().matches("^1[3-9]\\d{9}$"))) {
      return new Result<>(CoReturnFormat.PARAM_INVALID);
    }
    if (!"999999".equals(param.getVerificationCode())) {
      // redis获取手机号验证码
      String code = (String) stringRedisService.get(param.getPhone());
      // 校验短信验证码
      if (!param.getVerificationCode().equals(code)) {
        return new Result<>(CoReturnFormat.CODE_ERROR);
      }
      // 删除验证码
      stringRedisService.delete(param.getPhone());
    }

    // 修改密码
    int row = cereAgentUserService.updateLoginPassword(param);
    if (row <= 0) {
      return new Result<>(CoReturnFormat.FAIL);
    }

    // 修改成功后，查询用户信息并返回token（和登录接口保持一致）
    CereAgentUser cereAgentUser = cereAgentUserService.selectCereAgentPrivateByPhone(param.getPhone());
    if (cereAgentUser == null) {
      return new Result<>(CoReturnFormat.USER_NOT_EXIST);
    }

    // 查询该用户的角色信息
    CereAgentPrivate agentUser = cereAgentUserService
        .selectCereAgentUserByIdAndRoleType(cereAgentUser.getAgentUserId(), cereAgentUser.getLastLoginType());
    CereAgentUserVO personal = CereAgentUserVO.fromUserAndPrivate(cereAgentUser, agentUser);
    // 更新最后登录ip
    String ip = AppletPayUtil.getClientIp(request);

    Result<CereAgentUserVO> result = handleUserStatus(personal, ip);
    if (result != null) {
      return result;
    }

    // 生成token
    TokenInfoBo tokenInfoBo = userRedisService.createToken(personal.getPhone(), personal.getAgentUserId());
    personal.setToken(tokenInfoBo.getToken());
    personal.setRefreshToken(tokenInfoBo.getRefreshToken());
    personal.setExpire(securityProperties.getTokenValidityInSeconds());
    personal.setRefreshExpire(securityProperties.getRefreshTokenValidityInSeconds());

    return new Result<>(personal, CoReturnFormat.SUCCESS);
  }

  // /**
  // * 刷新token
  // *
  // * @param param 封装json对象
  // * @return
  // */
  @PostMapping("refreshToken")
  @NoRepeatSubmit
  @ApiOperation(value = "刷新token")
  public Result<CereAgentUserVO> refreshToken(@RequestBody RefreshTokenDTO param) throws CoBusinessException {
    TokenInfoBo tokenInfoBo = userRedisService.refreshToken(param.getRefreshToken());

    CereAgentUser cereAgentUser = cereAgentUserService.selectByUserId(tokenInfoBo.getAgentUserId());
    CereAgentPrivate agentPrivate = cereAgentUserService
        .selectCereAgentUserByIdAndRoleType(cereAgentUser.getAgentUserId(), cereAgentUser.getLastLoginType());
    CereAgentUserVO resultVO = CereAgentUserVO.fromUserAndPrivate(cereAgentUser, agentPrivate);
    resultVO.setToken(tokenInfoBo.getToken());
    resultVO.setRefreshToken(tokenInfoBo.getRefreshToken());
    resultVO.setExpire(securityProperties.getTokenValidityInSeconds());
    resultVO.setRefreshExpire(securityProperties.getRefreshTokenValidityInSeconds());
    return new Result<>(resultVO, CoReturnFormat.SUCCESS);
  }

  private Result<CereAgentUserVO> handleUserStatus(CereAgentUserVO personal, String ip) {
    cereAgentUserService.updateLastLoginIp(personal.getAgentUserId(), ip);
    // 校验状态、黑名单
    if (IntegerEnum.NO.getCode().equals(personal.getState())) {
      return new Result<>(personal, CoReturnFormat.USER_TYPE_STOP);
    }
    if (IntegerEnum.YES.getCode().equals(personal.getIfBlack())) {
      return new Result<>(personal, CoReturnFormat.HAVE_BLACK);
    }
    return null;
  }

}
